#include "disp_proc.h"
#include <esp_log.h>
#include <esp_check.h>
#include <esp_system.h>

#include <driver/ledc.h>
#include <driver/gpio.h>
#include <driver/spi_master.h>

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <freertos/timers.h>
#include <freertos/semphr.h>

#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_touch_ft5x06.h"
#include "esp_lcd_touch.h"
#include "esp_lvgl_port.h"
#include "lvgl.h"
#include "lv_demos.h"
#include "system_init.h"
#include "io_proc.h"


#include <cstring>

#include "pin_map.h"
#define TAG "disp"

#define BSP_LCD_BACKLIGHT       PIN_ESP_LCD_BRIGHTNESS
#define BSP_LCD_SPI_CLK         PIN_LCD_SCLK
#define BSP_LCD_SPI_MOSI        PIN_LCD_MOSI
#define BSP_LCD_DC              PIN_LCD_DC
#define BSP_LCD_RST             GPIO_NUM_NC
#define BSP_LCD_SPI_CS          GPIO_NUM_NC
#define BSP_LCD_H_RES           LCD_H_RES
#define BSP_LCD_V_RES           LCD_V_RES

#define BSP_LCD_SPI_NUM         LCD_SPI_BUS_NUM
#define BSP_LCD_BITS_PER_PIXEL  (16)
#define BSP_LCD_PIXEL_CLOCK_HZ  LCD_PIXEL_CLOCK_HZ
#define BSP_LCD_DRAW_BUF_HEIGHT (BSP_LCD_V_RES / 5)

#define LCD_LEDC_CH             LEDC_CHANNEL_0

// 背光PWM初始化
esp_err_t bsp_display_brightness_init(void)
{
    // Setup LEDC peripheral for PWM backlight control
    ledc_channel_config_t LCD_backlight_channel;
    memset(&LCD_backlight_channel, 0 , sizeof(ledc_channel_config_t));
    {
        LCD_backlight_channel.gpio_num = BSP_LCD_BACKLIGHT;
        LCD_backlight_channel.speed_mode = LEDC_LOW_SPEED_MODE;
        LCD_backlight_channel.channel = LCD_LEDC_CH;
        LCD_backlight_channel.intr_type = LEDC_INTR_DISABLE;
        LCD_backlight_channel.timer_sel = LEDC_TIMER_0;
        LCD_backlight_channel.duty = 0;
        LCD_backlight_channel.hpoint = 0;
        LCD_backlight_channel.flags.output_invert = true;
    };
    ledc_timer_config_t LCD_backlight_timer;
    memset(&LCD_backlight_timer, 0 , sizeof(ledc_channel_config_t));
    {
        LCD_backlight_timer.speed_mode = LEDC_LOW_SPEED_MODE;
        LCD_backlight_timer.duty_resolution = LEDC_TIMER_10_BIT;
        LCD_backlight_timer.timer_num = LEDC_TIMER_0;
        LCD_backlight_timer.freq_hz = 5000;
        LCD_backlight_timer.clk_cfg = LEDC_AUTO_CLK;
    };

    ESP_ERROR_CHECK(ledc_timer_config(&LCD_backlight_timer));
    ESP_ERROR_CHECK(ledc_channel_config(&LCD_backlight_channel));

    return ESP_OK;
}

// 背光亮度设置
esp_err_t bsp_display_brightness_set(int brightness_percent)
{
    if (brightness_percent > 100) {
        brightness_percent = 100;
    } else if (brightness_percent < 0) {
        brightness_percent = 0;
    }

    ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent);
    // LEDC resolution set to 10bits, thus: 100% = 1023
    uint32_t duty_cycle = (1023 * brightness_percent) / 100;
    ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle));
    ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH));

    return ESP_OK;
}

// 关闭背光
esp_err_t bsp_display_backlight_off(void)
{
    return bsp_display_brightness_set(0);
}

// 打开背光 最亮
esp_err_t bsp_display_backlight_on(void)
{
    return bsp_display_brightness_set(100);
}

// 定义液晶屏句柄
static esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL; 
static esp_lcd_touch_handle_t tp;   // 触摸屏句柄
static lv_disp_t *disp;      // 指向液晶屏
static lv_indev_t *disp_indev = NULL; // 指向触摸屏

// 液晶屏初始化
esp_err_t bsp_display_new(void)
{
    esp_err_t ret = ESP_OK;
    // 背光初始化
    ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed");
    // 初始化SPI总线
    ESP_LOGD(TAG, "Initialize SPI bus");
    spi_bus_config_t buscfg;
    memset(&buscfg, 0, sizeof(spi_bus_config_t));
    {
        buscfg.sclk_io_num = BSP_LCD_SPI_CLK;
        buscfg.mosi_io_num = BSP_LCD_SPI_MOSI;
        buscfg.miso_io_num = GPIO_NUM_NC;
        buscfg.quadwp_io_num = GPIO_NUM_NC;
        buscfg.quadhd_io_num = GPIO_NUM_NC;
        buscfg .max_transfer_sz = BSP_LCD_H_RES * BSP_LCD_V_RES * sizeof(uint16_t);
    };
    esp_lcd_panel_io_spi_config_t io_config;
    memset(&io_config, 0, sizeof(esp_lcd_panel_io_spi_config_t));
    {
        io_config.dc_gpio_num = BSP_LCD_DC;
        io_config.cs_gpio_num = BSP_LCD_SPI_CS;
        io_config.pclk_hz = BSP_LCD_PIXEL_CLOCK_HZ;
        io_config.lcd_cmd_bits = LCD_CMD_BITS;
        io_config.lcd_param_bits = LCD_PARAM_BITS;
        io_config.spi_mode = 2;
        io_config.trans_queue_depth = 10;
    };
    esp_lcd_panel_dev_config_t panel_config;
    {
        panel_config.reset_gpio_num = BSP_LCD_RST;
        panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
        panel_config.bits_per_pixel = BSP_LCD_BITS_PER_PIXEL;
    };
    ESP_RETURN_ON_ERROR(spi_bus_initialize(BSP_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed");
    // 液晶屏控制IO初始化
    ESP_LOGD(TAG, "Install panel IO");
    ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)BSP_LCD_SPI_NUM, &io_config, &io_handle), err, TAG, "New panel IO failed");
    // 初始化液晶屏驱动芯片ST7789
    ESP_LOGD(TAG, "Install LCD driver");
    ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle), err, TAG, "New panel failed");
    esp_lcd_panel_reset(panel_handle);  // 液晶屏复位
    lcd_cs(0);  // 拉低CS引脚
    esp_lcd_panel_init(panel_handle);  // 初始化配置寄存器
    esp_lcd_panel_invert_color(panel_handle, true); // 颜色反转
    esp_lcd_panel_swap_xy(panel_handle, true);  // 显示翻转 
    esp_lcd_panel_mirror(panel_handle, true, false); // 镜像

    return ret;

err:
    if (panel_handle) {
        esp_lcd_panel_del(panel_handle);
    }
    if (io_handle) {
        esp_lcd_panel_io_del(io_handle);
    }
    spi_bus_free(BSP_LCD_SPI_NUM);
    return ret;
}

// LCD显示初始化
esp_err_t bsp_lcd_init(void)
{
    esp_err_t ret = ESP_OK;
    ret = bsp_display_new(); // 液晶屏驱动初始化
    lcd_set_color(0x0000); // 设置整屏背景黑色
    ret = esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示
    ret = bsp_display_backlight_on(); // 打开背光显示

    return  ret;
}

// 液晶屏初始化+添加LVGL接口
static lv_disp_t *bsp_display_lcd_init(void)
{
    /* 初始化液晶屏 */
    bsp_display_new(); // 液晶屏驱动初始化
    lcd_set_color(0xffff); // 设置整屏背景白色
    esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示

    /* 液晶屏添加LVGL接口 */
    ESP_LOGD(TAG, "Add LCD screen");
    lvgl_port_display_cfg_t disp_cfg;
    memset(&disp_cfg, 0, sizeof(lvgl_port_display_cfg_t));
    {
        disp_cfg.io_handle = io_handle;
        disp_cfg.panel_handle = panel_handle;
        disp_cfg.buffer_size = BSP_LCD_H_RES * BSP_LCD_DRAW_BUF_HEIGHT;   // LVGL缓存大小 
        disp_cfg.double_buffer = true; // 是否开启双缓存
        disp_cfg.hres = BSP_LCD_H_RES; // 液晶屏的宽
        disp_cfg.vres = BSP_LCD_V_RES; // 液晶屏的高
        disp_cfg.monochrome = false;  // 是否单色显示器
        /* Rotation的值必须和液晶屏初始化里面设置的 翻转 和 镜像 一样 */
        disp_cfg.rotation.swap_xy = true;  // 是否翻转
        disp_cfg.rotation.mirror_x = true; // x方向是否镜像
        disp_cfg.rotation.mirror_y = false; // y方向是否镜像
        disp_cfg.flags.buff_dma = false;  // 是否使用DMA 注意：dma与spiram不能同时为true
        disp_cfg.flags.buff_spiram = true; // 是否使用PSRAM 注意：dma与spiram不能同时为true
    };

    return lvgl_port_add_disp(&disp_cfg);
}

// 触摸屏初始化
esp_err_t bsp_touch_new(esp_lcd_touch_handle_t *ret_touch)
{
    /* Initialize touch */
    esp_lcd_touch_config_t tp_cfg;
    memset(&tp_cfg, 0, sizeof(esp_lcd_touch_config_t));
    {
        tp_cfg.x_max = BSP_LCD_V_RES;
        tp_cfg.y_max = BSP_LCD_H_RES;
        tp_cfg.rst_gpio_num = GPIO_NUM_NC; // Shared with LCD reset
        tp_cfg.int_gpio_num = GPIO_NUM_NC; 

        tp_cfg.levels.reset = 0;
        tp_cfg.levels.interrupt = 0;
        tp_cfg.flags.swap_xy = 1;
        tp_cfg.flags.mirror_x = 1;
        tp_cfg.flags .mirror_y = 0;

    };
    esp_lcd_panel_io_handle_t tp_io_handle = NULL;
    esp_lcd_panel_io_i2c_config_t tp_io_config;
    memset(&tp_io_config, 0, sizeof(esp_lcd_panel_io_i2c_config_t));
    tp_io_config.dev_addr = ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS;
    tp_io_config.control_phase_bytes = 1;
    tp_io_config.dc_bit_offset = 0;
    tp_io_config.lcd_cmd_bits = 8;
    tp_io_config.flags.disable_control_phase = 1;   
    tp_io_config.scl_speed_hz = 100 * 1000;     

    ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(esp_i2c_bus_handle_get(0), &tp_io_config, &tp_io_handle), TAG, "");
    ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, ret_touch));

    return ESP_OK;
}

// 触摸屏初始化+添加LVGL接口
static lv_indev_t *bsp_display_indev_init(lv_disp_t *disp)
{
    /* 初始化触摸屏 */
    ESP_ERROR_CHECK(bsp_touch_new(&tp));
    assert(tp);

    /* 添加LVGL接口 */
    lvgl_port_touch_cfg_t touch_cfg;
    {
        touch_cfg.disp = disp;
        touch_cfg.handle = tp;
    };

    return lvgl_port_add_touch(&touch_cfg);
}

// 全局标签
// 根节点
static lv_obj_t *root = NULL;
static lv_obj_t *time_label = NULL;
static lv_obj_t *date_label = NULL;
// 定时器回调函数，每秒更新一次时间和日期
static void update_time_date(lv_timer_t *timer) {
    // 获取当前时间和日期
    time_t now = time(NULL);
    struct tm *timeinfo = localtime(&now);
    // 更新时间标签
    char time_str[64];
    strftime(time_str, sizeof(time_str), "%H:%M:%S", timeinfo); // 格式化时间
    lv_label_set_text(time_label, time_str); // 更新标签文本
    // 更新日期标签
    char date_str[64];
    strftime(date_str, sizeof(date_str), "%Y-%m-%d", timeinfo); // 格式化日期
    lv_label_set_text(date_label, date_str); // 更新标签文本

}

// 创建一个时间和日期的界面
void create_datetime_ui(lv_obj_t *parent) {
    // 创建一个标签用于显示时间
    time_label = lv_label_create(parent);
    lv_obj_align(time_label, LV_ALIGN_TOP_MID, 0, 10); // 居中对齐
    lv_label_set_text(time_label, "Time: "); // 设置标签文本

    // 创建一个标签用于显示日期
    date_label = lv_label_create(parent);
    lv_obj_align(date_label, LV_ALIGN_BOTTOM_MID, 0, -10); // 居中对齐
    lv_label_set_text(date_label, "Date: "); // 设置标签文本

    // 创建一个定时器，每秒更新一次时间和日期
    lv_timer_t *timer = lv_timer_create(update_time_date, 1000, NULL); 
    // 启动定时器
    lv_timer_ready(timer);

}

// 开发板显示初始化
void bsp_lvgl_start(void)
{
    /* 初始化LVGL */
    lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
    lvgl_port_init(&lvgl_cfg);

    /* 初始化液晶屏 并添加LVGL接口 */
    disp = bsp_display_lcd_init();

    /* 初始化触摸屏 并添加LVGL接口 */
    disp_indev = bsp_display_indev_init(disp);

    /* 打开液晶屏背光 */
    bsp_display_backlight_on();

    // lv_demo_keypad_encoder();
    // 创建根节点
    root = lv_obj_create(NULL);
    // 创建时间和日期界面
    create_datetime_ui(root);
}

// 显示图片
void lcd_draw_pictrue(int x_start, int y_start, int x_end, int y_end, const unsigned char *gImage)
{
    // 分配内存 分配了需要的字节大小 且指定在外部SPIRAM中分配
    size_t pixels_byte_size = (x_end - x_start)*(y_end - y_start) * 2;
    uint16_t *pixels = (uint16_t *)heap_caps_malloc(pixels_byte_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
    if (NULL == pixels)
    {
        ESP_LOGE(TAG, "Memory for bitmap is not enough");
        return;
    }
    memcpy(pixels, gImage, pixels_byte_size);  // 把图片数据拷贝到内存
    esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, (uint16_t *)pixels); // 显示整张图片数据
    heap_caps_free(pixels);  // 释放内存
}

// 设置液晶屏颜色
void lcd_set_color(uint16_t color)
{
    // 分配内存 这里分配了液晶屏一行数据需要的大小
    uint16_t *buffer = (uint16_t *)heap_caps_malloc(BSP_LCD_H_RES * sizeof(uint16_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
    
    if (NULL == buffer)
    {
        ESP_LOGE(TAG, "Memory for bitmap is not enough");
    }
    else
    {
        for (size_t i = 0; i < BSP_LCD_H_RES; i++) // 给缓存中放入颜色数据
        {
            buffer[i] = color;
        }
        for (int y = 0; y < 240; y++) // 显示整屏颜色
        {
            esp_lcd_panel_draw_bitmap(panel_handle, 0, y, 320, y+1, buffer);
        }
        free(buffer); // 释放内存
    }
}
static QueueHandle_t disp_frame_que = nullptr;

void disp_camera_push(camera_fb_t **fb)
{
    xQueueSend(disp_frame_que, fb, portMAX_DELAY);
}

static void disp_camera_task(void *argv)
{
    camera_fb_t *frame = NULL;
    while (1) {
        if (xQueueReceive(disp_frame_que, &frame, portMAX_DELAY)) {
            esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, frame->width, frame->height, (uint16_t *)frame->buf);
            esp_camera_fb_return(frame);
        }
    }
}


void disp_camera_task_create(void)
{
    disp_frame_que = xQueueCreate(2, sizeof(camera_fb_t *));
    xTaskCreatePinnedToCore(disp_camera_task, "disp_camera", 4 * 1024, NULL, 5, NULL, 0);
}